<template>
  <uploader
    ref="uploader"
    :options="uploadOptions"
    :autoStart="false"
    :fileStatusText="fileStatusText"
    :list="false"
    v-on="event"
    @file-added="onFileAdded"
    @file-success="onFileSuccess"
    @file-progress="onFileProgress"
    @file-error="onFileError"
    @file-removed="onFileRemove"
    @upload-start="onUploadStart"
    @complete="onComplete"
    v-loading="loading"
  >
    <uploader-list class="bottomRight--upload-list" v-if="positionType === 'bottomRight'">
      <template v-slot="{ fileList }">
        <el-dialog class="file-upload--dialog" title="文件列表" :width="dialogSmallFlag?'200px':'440px'"
                   :modal="false"
                   ref="fileUploadDialog"
                   :modal-append-to-body="false"
                   :close-on-click-modal="false"
                   :before-close="closeDialog"
                   :visible.sync="test">
          <div>
            <div class="el-dialog__custom-header" slot="title">
              <div class="title">文件列表</div>
              <div class="headerbtn">
                <i class="dialog-small" :class="dialogSmallFlag?'el-icon-crop':'el-icon-minus'" @click="dialogSmall()"></i>
                <i class="dialog-close el-icon-close" @click="dialogClose()"></i>
              </div>
            </div>
            <uploader-file v-if="!dialogSmallFlag" v-for="file in fileList" :key="file.id" :class="'file_' + file.id" :file="file" :list="true"></uploader-file>
          </div>
        </el-dialog>

      </template>
    </uploader-list>
    <uploader-list class="normal--upload-list" v-else>
      <template v-slot="{ fileList }">
        <uploader-file v-for="file in fileList" :key="file.id" :class="'file_' + file.id" :file="file" :list="true"></uploader-file>
      </template>
    </uploader-list>

    <uploader-unsupport>当前浏览器不支持H5文件上传</uploader-unsupport>
    <uploader-btn v-bind="btnOptions">
      <el-tooltip class="item" effect="dark" :content="extra.btnOptions.tipText" placement="bottom-start">
        <span class="el-button el-button--primary el-button--small">
        <i class="el-icon-upload" />
        <span>{{ extra.btnOptions.btnText || '上传文件' }}</span>
      </span>
      </el-tooltip>

    </uploader-btn>
    <div class="desc">{{ this.extra.desc }}</div>
  </uploader>
</template>

<script>
  // import { signer } from '@/util/tools'
  import SparkMD5 from 'spark-md5'
  import request from '@/router/axios'
  // import { filecenterUrl } from '@/config/env.js'
  import Vue from 'vue'
  import uploader from 'vue-simple-uploader'
  import { getStore } from '@/util/store'

  Vue.use(uploader)

  export default {
    name: 'chunkUpload',
    props: {
      positionType: {
        type: String,
        default: 'default', //default默认从上到下显示，bottomRight右下角显示,一般正常情况请使用default
      },
      component: {
        type: String,
        default: 'input'
      },
      hidden: {
        type: Boolean,
        default: false
      },
      options: {
        type: Object,
        default() {
          return {}
        }
      },
      event: {
        type: Object,
        default() {
          return {}
        }
      },
      extra: {
        type: Object,
        default() {
          return {
            desc: '',
            btnOptions: {}
          }
        }
      }
    },
    data() {
      return {
        uploadIdObj: {},
        test: false,
        document: document,
        fileList: [],
        loading: false,
        fileStatusText: {
          success: '成功',
          error: '失败',
          uploading: '上传中',
          paused: '暂停',
          waiting: '等待'
        },
        defaultOptions: {
          target: `/admin/chunkfile/upload`,
          chunkSize: '10240000',
          fileSizeType: 2,
          fileParameterName: 'file', //上传文件时文件的参数名，默认file
          maxChunkRetries: 3, //最大自动失败重试上传次数
          withCredentials: true,
          testMethod: 'GET',
          testChunks: true, //是否开启服务器分片校验
          checkChunkUploadedByResponse: (chunk, res) => {
            // 服务器分片校验函数，秒传及断点续传基础
            res = JSON.parse(res)
            // const details = res.plain.details
            const details = res.data
            this.uploadIdObj[details.fileHash] = details.uploadId
            if (details.skipUpload) {
              return true
            }
            return (details.uploaded || []).indexOf(chunk.offset + 1) >= 0
          },
          processParams: params => {
            params.fileHash = params.identifier
            params.fileName = params.filename
            params.uploadId = this.uploadIdObj[params.fileHash]
            if (this.fileSizeType) {
              params.fileSizeType = this.fileSizeType
            }
            let packet = JSON.stringify({plain:params})
            return {
              packet: packet
            }
          },
          parseTimeRemaining: function (timeRemaining, parsedTimeRemaining) {
            return parsedTimeRemaining
              .replace(/\syears?/, '年')
              .replace(/\days?/, '天')
              .replace(/\shours?/, '小时')
              .replace(/\sminutes?/, '分钟')
              .replace(/\sseconds?/, '秒')
          },
          // processResponse: (response, cb, file, chunk) => {
          //   const res = JSON.parse(response)
          //   if(res.plain.resultCode !== '000') {
          //     file.error = true
          //   }
          //   cb(null, response)
          // },
          headers: {
            // 在header中添加的验证，请根据实际业务来
            'Accept': 'application/json, text/plain, */*',
            'TENANT-ID': getStore({name: 'tenantId'}),
            'Authorization': 'Bearer ' + getStore({name: 'access_token'}),
          },
          query: {},
          successStatuses:[200,201,202],
          permanentErrors: [],//404, 415, 500, 501
        },
        fileSizeType: '',
        completed: false,
        dialogSmallFlag: false,
      }
    },
    mounted() {},
    computed: {
      uploader() {
        return this.$refs.uploader.uploader
      },
      uploadOptions() {
        return Object.assign(this.defaultOptions, this.options)
      },
      btnOptions() {
        return Object.assign(
          {
            directory: false,
            single: true,
            attrs: {
              accept: ''
            }
          },
          this.extra.btnOptions
        )
      }
    },
    methods: {
      onUploadStart() {
        this.completed = false
        this.$emit('completed', false)
        console.log('start', this.fileList.length)
      },
      onComplete() {
        this.completed = true
        this.$emit('completed', true)
        console.log('complete', this.fileList.length)
      },
      onFileAdded(file) {
        this.test = true
        console.log('file',file)
        //文件大小超过this.btnOptions.bigFileSize添加 fileSizeType=2
        if (this.btnOptions.bigFileSize) {
          let bigFileSize = this.btnOptions.bigFileSize * 1024 * 1024
          this.fileSizeType = file.size >= bigFileSize ? 2 : ''
        }
        if (file.size > this.btnOptions.size) {
          file.ignored = true
          return this.$message.warning('文件必须小于100M')
        }
        if (this.btnOptions.single && this.fileList.length === 1) {
          file.ignored = true
          return this.$message.warning('只能上传一个文件')
        }
        this.computeMD5(file)
      },
      onFileProgress(rootFile, file, chunk) {
        console.log(`上传中 ${file.name}，chunk：${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`)
      },
      onFileSuccess(rootFile, file, response, chunk) {
        console.log('onFileSuccess',response)
        const res = JSON.parse(response)
        // const plain = res.plain
        const plain = res.data
        if (this.btnOptions.single && this.fileList.length === 1) {
          this.statusSet(file.id, 'failed')
          return this.$message.warning('只能上传一个文件')
        }
        // if (plain.resultCode !== '000') {
        //   this.statusSet(file.id, 'failed')
        //   return this.$message.warning(plain.resultMsg)
        // }
        if (res.code != '0') {
          this.statusSet(file.id, 'failed')
          return this.$message.warning(res.msg)
        }

        if (plain.needMerge) {
          this.statusSet(file.id, 'merging')
          let values = {
            // baseUrl: filecenterUrl,
            url: '/admin/chunkfile/merge',
            fileSizeType: 2,
            method: 'post',
            data: {plain:{...plain,
                fileSizeType: this.fileSizeType,
                uploadId:this.uploadIdObj[plain.fileHash]
            }
            }
          }
          if (this.fileSizeType) {
            values.timeout = 300000
          }
          request(values)
            .then(res => {
              // 暂时注释掉去重的代码
              // if (!this.fileList.some(item => item.id === res.details.id)) {
              this.fileList.push(res.details)
              // }
              this.onFileChange(file)
              this.statusRemove(file.id)
              this.$message.success('上传成功'),
              delete this.uploadIdObj[plain.fileHash]
            })
            .catch(e => {
              this.$message.error(e.message)
              this.statusSet(file.id, 'mergeFailed')
            })
        } else {
          // if (!this.fileList.some(item => item.id === plain.details.id)) {
          this.fileList.push(plain)
          // }
          this.onFileChange(file)
          this.$message.success('上传成功')
        }
      },
      onFileError(rootFile, file, response, chunk) {
        this.$message.warning('上传失败')
        if (this.btnOptions.single) {
          this.fileList = []
          this.onFileChange(file)
        }
      },
      onFileChange(file) {
        this.$emit('change', {
          file,
          fileList: this.fileList
        })
        if (this.btnOptions.single && this.fileList.length > 1) {
          return this.$message.warning('只能上传一个文件')
        }
      },
      onFileRemove(file) {
        let fileList = [...this.fileList]

        fileList.some((item, index) => {
          if (item.fileHash === file.uniqueIdentifier && encodeURIComponent(item.fileName) === encodeURIComponent(file.name)) {
            fileList.splice(index, 1)
            return true
          }
        })
        this.fileList = fileList
        this.onFileChange(file)
      },
      statusSet(id, status) {
        let statusMap = {
          md5: '生成MD5',
          merging: '合并中',
          mergeFailed: '合并失败',
          failed: '上传失败'
        }
        this.$nextTick(() => {
          let state = document.createElement('em')
          state.id = 'status_' + id
          state.innerText = statusMap[status]
          state.style.position = 'absolute'
          state.style.backgroundColor = this.positionType === 'bottomRight' ? '#e2eeff' : '#fff'
          state.style.top = '0'
          state.style.left = '0'
          state.style.right = '0'
          state.style.bottom = '0'
          const node = document.querySelector('.file_' + id + ' .uploader-file-status span')
          node.appendChild(state)
        })
      },
      statusRemove(id) {
        this.$nextTick(() => {
          let node = document.querySelector('#status_' + id)
          if (document.querySelector('.file_' + id + ' .uploader-file-status span')) {
            document.querySelector('.file_' + id + ' .uploader-file-status span').removeChild(node)
          }
        })
      },
      computeMD5(file) {
        let fileReader = new FileReader()
        let time = new Date().getTime()
        let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
        let currentChunk = 0
        const chunkSize = 10 * 1024 * 1000
        let chunks = Math.ceil(file.size / chunkSize)
        let spark = new SparkMD5.ArrayBuffer() //new SparkMD5()
        this.loading = true
        // file.pause()

        loadNext()
        // this.statusSet(file.id, 'md5')
        fileReader.onload = e => {
          spark.append(e.target.result)
          //spark.appendBinary(e.target.result)
          if (currentChunk < chunks) {
            currentChunk++
            loadNext()
          } else {
            let md5 = spark.end()
            file.uniqueIdentifier = md5
            // this.statusRemove(file.id)
            file.resume()
            this.loading = false
            console.log(`MD5计算完毕：${file.name} \nMD5：${md5} \n分片：${chunks} 大小:${file.size} 用时：${new Date().getTime() - time} ms`)
          }
        }
        fileReader.onerror = function() {
          this.$message.warning(`文件${file.name}读取出错，请检查该文件`)
          file.cancel()
        }
        function loadNext() {
          let start = currentChunk * chunkSize
          let end = start + chunkSize >= file.size ? file.size : start + chunkSize
          fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end))
          //fileReader.readAsBinaryString(blobSlice.call(file.file, start, end))
        }
      },
      closeDialog(done){
        this.$emit('close',done)
      },
      dialogSmall(){
        this.dialogSmallFlag = !this.dialogSmallFlag
      },
      dialogClose(){
        const hide = () => {
          this.test = false
        }

        if (!this.completed) {
          // 文件还在上传中时，关闭弹窗
          this.$confirm('文件在上传中，确认关闭该窗口？', '温馨提示', {
            confirmButtonText: '直接关闭',
            cancelButtonText: '取消',
            type: 'warning'
          })
            .then(() => {
              hide()
            })
            .catch(() => {})
          return
        }

        // if (this.uploadList.length) {
        //   // 文件已经上传完成，但并未保存时，关闭弹窗
        //   this.$confirm('当前文件已上传完成，是否进行保存上传?', '温馨提示', {
        //     distinguishCancelAndClose: true, // 将回调的action分为close和cancel
        //     confirmButtonText: '保存',
        //     cancelButtonText: '直接关闭',
        //     type: 'warning'
        //   })
        //     .then(() => {
        //       this.sumbit()
        //     })
        //     .catch(action => {
        //       if (action === 'cancel') {
        //         hide()
        //       }
        //     })
        //   return
        // }

        hide()

      },
    },
    watch: {},
    destroyed() {},
    components: {}
  }
</script>

<style lang="scss">
  .uploader {
    .desc {
      position: relative;
      padding-top: 5px;
      color: #999;
      line-height: 23px;
      word-break: break-all;
    }
    .uploader-btn {
      padding: 0;
      border: 0;
      margin-top: 4px;
      .ant-btn {
        line-height: 32px;
      }
      &:hover{
        background-color: transparent;
      }
    }
    .uploader-list .uploader-file {

      .uploader-file-meta {
        width: 0;
      }
      .uploader-file-status {
        width: 32%;
        text-indent: 0;
      }

      .uploader-file-icon:before {
        font-size: 14px;
        line-height: 22px;
      }
      &[status='success'] .uploader-file-info:hover {
        background-color: #e6f7ff;
      }

      &[status='error'] .uploader-file-progress {
        background: #ffe0e0;
      }
      .uploader-file-name {
        text-indent: 5px;
      }
      .uploader-file-size {
        // font-size: 12px;
        // height: auto;
      }
      .uploader-file-actions {
        position: absolute;
        right: 0;
        top: 0;
        width: auto;
      }
      &[status='success'] .uploader-file-remove {
        display: block;
      }

    }
    .bottomRight--upload-list .uploader-file {
      line-height: 36px;
      vertical-align: middle;
      height: 36px;
      padding-bottom: 0;
      font-size: 12px;
      .uploader-file-icon {
        margin-top: 8px;
      }
      .uploader-file-actions {
        span {
          margin-top: 9px;
        }
      }
    }
    .normal--upload-list .uploader-file {
      border-bottom: 0;
      line-height: 22px;
      vertical-align: middle;
      margin-top: 3px;
      height: 27px;
      padding-bottom: 5px;
      font-size: 12px;

      .uploader-file-icon {
        margin-top: 0;
      }
      .uploader-file-progress {
        background: #fff;
        height: 1px;
        bottom: 0;
      }
      .uploader-file-progressing {
        background: #1890ff;
      }
      .uploader-file-actions {
        span {
          margin-top: 2px;
        }
      }
    }
  }
  .file-upload--dialog{
    &.el-dialog__wrapper{
      top: auto;
      left: auto;
    }
    .el-dialog{
      position: fixed;
      right: 0;
      bottom: 0;
      margin: 10px;
      .el-dialog__custom-header{
        display: flex;
        justify-content: space-between;
        padding: 10px;
        color: #333;
        border-bottom: 1px solid #EBEEF5;
        .title{
          font-size: 13px;
        }
        .headerbtn{
          top: 10px;
          right: 10px;
          i{
            font-size: 16px;
            color: #409EFF;
            font-weight: bold;
            margin-left: 10px;
            cursor: pointer;
          }
        }
      }
      .el-dialog__header{
        display: none;
        padding: 5px 10px;
        border-bottom: 1px solid #EBEEF5;
        .el-dialog__title{
          font-size: 13px;
        }
        .el-dialog__headerbtn{
          top: 10px;
          right: 10px;
          .el-dialog__close{
            color: #409EFF;
            font-weight: bold;
          }
        }
      }
      .el-dialog__body{
        padding: 0;
      }
    }
  }
</style>
